package olga.gorbatiuk.perdep.sorting;

/**
 * @author Olga Gorbatiuk
 */
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

//Noninstantiable utility class
public class Benchmark {

    private Benchmark() {
        // Suppress default constructor for noninstantiability
        throw new AssertionError();
    }

    public static void evaluate(List<Class<?>> sortingAlgorithms,
            List<Class<?>> arrayFillers,
            List<ArrayParameters> arrayParametersList)
            throws NoSuchMethodException, IllegalAccessException,
            InvocationTargetException, InstantiationException {
        List<Long> estimatedTimeArray = new ArrayList<Long>();
        String matrix[][] = new String[(arrayParametersList.size() * arrayFillers
                .size()) + 1][sortingAlgorithms.size() + 2];

        for (ArrayParameters arrayParameters : arrayParametersList) {
            runWithParameters(sortingAlgorithms, arrayFillers, arrayParameters,
                    arrayParametersList, estimatedTimeArray);
        }
        printValue(sortingAlgorithms, arrayFillers, matrix,
                arrayParametersList, estimatedTimeArray);
    }

    private static void runWithParameters(List<Class<?>> sortingAlgorithms,
            List<Class<?>> arrayFillers, ArrayParameters arrayParameters,
            List<ArrayParameters> arrayParametersList,
            List<Long> estimatedTimeArray) throws NoSuchMethodException,
            IllegalAccessException, InvocationTargetException,
            InstantiationException {
        int min = arrayParameters.getMin();
        int max = arrayParameters.getMax();
        int size = arrayParameters.getSize();

        for (int i = 0; i < arrayFillers.size(); i++) {
            Class<?> arrayFiller = arrayFillers.get(i);
            int[] array = ReflectionUtils.invokeGetFilledArrayMethod(
                    arrayFiller, min, max, size);

            for (Class<?> sortingAlgorithm : sortingAlgorithms) {
                Long startTime = System.nanoTime(); // Start time
                ReflectionUtils.invokeSortMethod(sortingAlgorithm, array);
                Long estimatedTime = System.nanoTime() - startTime; // End time
                estimatedTimeArray.add(estimatedTime);
            }
        }
    }

    private static void printValue(List<Class<?>> sortingAlgorithms,
            List<Class<?>> arrayFillers, String matrix[][],
            List<ArrayParameters> arrayParametersList,
            List<Long> estimatedTimeArray) {

        populateMatrixByArrayFillersName(arrayFillers, matrix,
                arrayParametersList, estimatedTimeArray);
        populateMatrixBySortingAlgorinmsName(sortingAlgorithms, matrix);

        populateMatrixBySizeOfArrays(arrayFillers, matrix, arrayParametersList);
        populateModelByEstimatedTimeValue(sortingAlgorithms, matrix,
                estimatedTimeArray);
        printMatrix(matrix);
    }

    private static void populateMatrixByArrayFillersName(
            List<Class<?>> arrayFillers, String[][] matrix,
            List<ArrayParameters> arrayParametersList,
            List<Long> estimatedTimeArray) {
        int cellForGraphicName = 1;
        for (int i = 0; i < arrayFillers.size(); i++) {
            matrix[cellForGraphicName][0] = arrayFillers.get(i).getSimpleName();
            matrix[cellForGraphicName + 1][0] = "\t\t";
            matrix[cellForGraphicName + 2][0] = "\t\t";
            matrix[cellForGraphicName + 3][0] = "\t\t";
            cellForGraphicName += (arrayParametersList.size());
        }
    }

    private static void populateMatrixBySortingAlgorinmsName(
            List<Class<?>> sortingAlgorithms, String[][] matrix) {
        int cell = 2;
        for (int j = 0; j < sortingAlgorithms.size(); j++) {
            matrix[0][0] = "\t";
            matrix[0][1] = "\t";
            matrix[0][cell] = sortingAlgorithms.get(j).getSimpleName();
            cell++;
        }
    }

    private static void populateMatrixBySizeOfArrays(
            List<Class<?>> arrayFillers, String[][] matrix,
            List<ArrayParameters> arrayParametersList) {
        int strForArrayLenght = 1;
        for (int h = 0; h < arrayFillers.size(); h++) {
            for (int j = 0; j < arrayParametersList.size(); j++) {
                matrix[strForArrayLenght][1] = String
                        .valueOf(arrayParametersList.get(j).getSize());
                strForArrayLenght++;
            }
        }
    }

    private static void populateModelByEstimatedTimeValue(
            List<Class<?>> sortingAlgorithms, String[][] matrix,
            List<Long> estimatedTimeArray) {
        int cellForEstimatedTime = 2;
        int strForEstimatedTime = 1;
        int flag = 1;
        for (int i = 0; i < estimatedTimeArray.size(); i++) {
            if (flag <= sortingAlgorithms.size()) {
                matrix[strForEstimatedTime][cellForEstimatedTime] = String
                        .valueOf(estimatedTimeArray.get(i));
                cellForEstimatedTime++;
                flag++;
            } else {
                flag = 1;
                strForEstimatedTime++;
                cellForEstimatedTime = 2;
                i--;
            }
        }
    }

    private static void printMatrix(String[][] matrix) {
        for (int j = 0; j < matrix.length; j++) {
            for (int k = 0; k < matrix[j].length; k++) {
                System.out.print(matrix[j][k] + ' ');
            }
            System.out.println();
        }
    }

}
